## load up the packages we will need:
require(igraph)
require(dplyr)
require(here)
require(ggcorrplot)
## ---------------------------
options(scipen = 6, digits = 4) # I prefer to view outputs in non-scientific notation
pallette <- c("#667c74", "#b3692b", "#c1ba7f", "#1f1918", "#bb8082")# this is needed on some PCs to increase memory allowance, but has no impact on mac
##------------------------Functions needed
readCSVs <- function(filename){
object <- read.csv(paste0(here("data","dfs_final"),"/",filename))
return(object)
}
#Test for normality
normalityTest <- function(df){
df <- df
object <- lapply(relevantCols,normalityTest_inside,df)
}
normalityTest_inside <- function(rel,df){
return(shapiro.test(df[,rel]))
}
getPvalueInside <- function(df){
if(df[[2]]<0.20){
return("no normality")
} else if(df[[2]]>0.20){
return("normality given")
}
}
getPvalue <- function(df){
list <- lapply(df,getPvalueInside)
return(list)
}
#Create Plots
createPlot <- function(index){
corr <- round(cor(DFs[[index]] %>%
select(k_shell,num_clusters,degree,community_centrality,total_infection,communities_louvain)
,method = "spearman"), 2)
p.mat <- cor_pmat(DFs[[index]] %>%
select(k_shell,num_clusters,degree,community_centrality,total_infection,communities_louvain))
p <- ggcorrplot(corr, hc.order = TRUE, type = "lower",
p.mat = p.mat,
lab = TRUE,
outline.col = "white",
ggtheme = ggplot2::theme_gray,
colors = c("#6D9EC1", "white", "#E46726"))+labs(title = DFs_files_list[[index]])
return(p)
}
Import Networks
First things first. Let’s import the newly generated DFs with Community Size Info
#Create list of DFs
DFs_files_list <- list.files(paste0(here("data","dfs_final"),"/"))
#Import DFs
DFs <- lapply(DFs_files_list,readCSVs)
Check normality
Check relevant measures for assumption of normality
relevantCols <- c(2,3,4,5,10,11)
relevantColNames <- c("k_shell","degree","community_centrality","num_clusters","communities_louvain","communities_LinkComm")
save <- lapply(DFs,normalityTest)
normalities <- lapply(save,getPvalue)
normalities <- lapply(normalities,as.data.frame)
normalities <- lapply(normalities,setNames,relevantColNames)
normalities
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
NA
Correlations
No normality can be assumed for any of the measures. Get Correlations of ENC_lv (Louvain) and ENC_lc (LinkComm) with other Measures
measuresVector <- c("k_shell","degree","community_centrality","peak_infection","total_infection")
getCorrelations_ENC_lv <- function(df){
output <- numeric()
c(output,as.numeric(df %>%
summarise(cor(communities_louvain,k_shell,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_louvain,degree,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_louvain,community_centrality,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_louvain,peak_infection,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_louvain,total_infection,method = "spearman")))) -> output
return(as.numeric(output))
}
getCorrelations_ENC_lc <- function(df){
output <- numeric()
c(output,as.numeric(df %>%
summarise(cor(communities_LinkComm,k_shell,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_LinkComm,degree,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_LinkComm,community_centrality,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_LinkComm,peak_infection,method = "spearman")))) -> output
c(output,as.numeric(df %>%
summarise(cor(communities_LinkComm,total_infection,method = "spearman")))) -> output
return(as.numeric(output))
}
corr_df_lc <- as.data.frame(cbind(measuresVector,as.data.frame(sapply(DFs,getCorrelations_ENC_lc))))
corr_df_lv <- as.data.frame(cbind(measuresVector,as.data.frame(sapply(DFs,getCorrelations_ENC_lv))))
names(corr_df_lc) <- c("measure",DFs_files_list)
names(corr_df_lv) <- c("measure",DFs_files_list)
corr_df_lc
corr_df_lv
Mean and SD Correlations
Get mean correlation and sd correlation for corr_df_lc (LinkComm) and corr_df_lv (Louvain)
corr_df_lc %>%
rowwise(measure)%>%
summarise(mean_corr_df_lc = mean(c(network_1.csv,
network_2.csv,
network_3.csv,
network_4.csv,
network_5.csv,
network_6.csv,
network_7.csv,
network_8.csv)),
sd_corr_df_lc=sd(c(network_1.csv,
network_2.csv,
network_3.csv,
network_4.csv,
network_5.csv,
network_6.csv,
network_7.csv,
network_8.csv)))
`summarise()` regrouping output by 'measure' (override with `.groups` argument)
corr_df_lv %>%
rowwise(measure)%>%
summarise(mean_corr_df_lv = mean(c(network_1.csv,
network_2.csv,
network_3.csv,
network_4.csv,
network_5.csv,
network_6.csv,
network_7.csv,
network_8.csv)),
sd_corr_df_lv=sd(c(network_1.csv,
network_2.csv,
network_3.csv,
network_4.csv,
network_5.csv,
network_6.csv,
network_7.csv,
network_8.csv)))
`summarise()` regrouping output by 'measure' (override with `.groups` argument)
Plots of Correlations
Now, we want to plot these values.
library(reshape2)
library(wesanderson)
#prepare dfs for plot (remove one col)
corr_df_lc %>%
select(-measure) -> corr_df_lc_plot
corr_df_lv %>%
select(-measure) -> corr_df_lv_plot
#plot first with melt
cbind(melt(corr_df_lc_plot,measure.vars = colnames(corr_df_lc_plot)),measure = as.factor(measuresVector)) %>% ggplot(.,aes(x=variable,y=value,color=measure))+geom_line(aes(group=measure))+geom_point(aes(color=measure),size=3,shape = 21,
stroke = 2.0,fill="white")+scale_color_manual(values=pallette)+ylab("Spearman's Correlation")+xlab("network")+labs(title = "Correlations of ENC_lc with other Measures")

#plot second with melt
cbind(melt(corr_df_lv_plot,measure.vars = colnames(corr_df_lv_plot)),measure = as.factor(measuresVector)) %>% ggplot(.,aes(x=variable,y=value,color=measure))+geom_line(aes(group=measure))+geom_point(aes(color=measure),size=3,shape = 21,
stroke = 2.0,fill="white")+scale_color_manual(values=pallette)+ylab("Spearman's Correlation")+xlab("network")+labs(title = "Correlations of ENC_lv with other Measures")

#Avg. Peak Infection across Networks
getAvgPeakInfection <- function(df){
df%>%
summarise(mean(peak_infection)) -> output
return(as.numeric(output))
}
avgPeakInfections <- cbind(DFs_files_list,as.data.frame(sapply(DFs,getAvgPeakInfection)))
names(avgPeakInfections) <- c("network","avgPeakInfection")
avgPeakInfections
##Plot of avg. Peak Infections
avgPeakInfections %>%
ggplot(aes(x=network,y=avgPeakInfection))+geom_line(aes(group = 1),color="#b3692b")+geom_point(color="#b3692b",size=3,shape = 21,stroke = 2.0,fill="white")+ylab("Peak Infection")+xlab("network")+labs(title = "Avg Peak Infection across networks")

NA
NA
##Imprecision Function
getImprecision_total <- function(df){
top_spreader <- as.numeric(df%>%
arrange(desc(total_infection))%>%
slice_head(prop = 0.1)%>%
summarise(mean(total_infection)))
output <- numeric()
c(output,as.numeric(1-(df%>%
arrange(desc(k_shell))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(total_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(degree))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(total_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(community_centrality))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(total_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(communities_louvain))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(total_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(communities_LinkComm))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(total_infection)))/top_spreader)) -> output
return(output)
}
getImprecision_peak <- function(df){
top_spreader <- as.numeric(df%>%
arrange(desc(peak_infection))%>%
slice_head(prop = 0.1)%>%
summarise(mean(peak_infection)))
output <- numeric()
c(output,as.numeric(1-(df%>%
arrange(desc(k_shell))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(peak_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(degree))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(peak_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(community_centrality))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(peak_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(communities_louvain))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(peak_infection)))/top_spreader)) -> output
c(output,as.numeric(1-(df%>%
arrange(desc(communities_LinkComm))%>%
slice_head(prop = 0.1)%>%
summarise(top_measure = mean(peak_infection)))/top_spreader)) -> output
return(output)
}
measures <- c("k_shell","degree","community_centrality","communities_louvain","communities_LinkComm")
imprecision_total <- cbind(measures,as.data.frame(sapply(DFs,getImprecision_total)))
imprecision_peak <- cbind(measures,as.data.frame(sapply(DFs,getImprecision_peak)))
names(imprecision_total) <- c("measure",DFs_files_list)
names(imprecision_peak) <- c("measure",DFs_files_list)
imprecision_peak
imprecision_total
##Imprecision Plots Now plot it
imprecision_total %>%
select(-measure) -> imprecision_total_plot
imprecision_peak %>%
select(-measure) -> imprecision_peak_plot
#plot first with melt
cbind(melt(imprecision_total_plot,measure.vars = colnames(imprecision_total_plot)),measure = as.factor(measures)) %>% ggplot(.,aes(x=variable,y=value,color=measure))+geom_line(aes(group=measure))+geom_point(aes(color=measure),size=3,shape = 21,
stroke = 2.0,fill="white")+scale_color_manual(values=pallette)+ylab("Imprecision")+xlab("network")+labs(title = "Imprecision Function (total_infection)")

#plot second with melt
cbind(melt(imprecision_peak_plot,measure.vars = colnames(imprecision_peak_plot)),measure = as.factor(measures)) %>% ggplot(.,aes(x=variable,y=value,color=measure))+geom_line(aes(group=measure))+geom_point(aes(color=measure),size=3,shape = 21,
stroke = 2.0,fill="white")+scale_color_manual(values=pallette)+ylab("Imprecision")+xlab("network")+labs(title = "Imprecision Function (peak_infection)")

##Bivariate Plots Now we want to take a look at the bivariate plots. Firstly, create the functions.
library(viridis)
createBivariatePlot <- function(index,measure_x,measure_y,infection){
DFs[[index]]%>%
ggplot(aes_string(x=measure_x,y=measure_y))+
geom_jitter(aes_string(color=infection))+scale_color_viridis(option = "inferno")+
theme_minimal()+labs(title = DFs_files_list[index])->plot
return(plot)
}
Now, create the plots
plots_cc_ks_total <- lapply(1:8,createBivariatePlot,"community_centrality","k_shell","total_infection")
plots_cc_ks_peak <- lapply(1:8,createBivariatePlot,"community_centrality","k_shell","peak_infection")
plots_cc_d_total <- lapply(1:8,createBivariatePlot,"community_centrality","degree","total_infection")
plots_cc_d_peak <- lapply(1:8,createBivariatePlot,"community_centrality","degree","peak_infection")
plots_enclv_ks_total <- lapply(1:8,createBivariatePlot,"communities_louvain","k_shell","total_infection")
plots_enclv_ks_peak <- lapply(1:8,createBivariatePlot,"communities_louvain","k_shell","peak_infection")
plots_enclv_cc_total <- lapply(1:8,createBivariatePlot,"communities_louvain","community_centrality","total_infection")
plots_enclv_cc_peak <- lapply(1:8,createBivariatePlot,"communities_louvain","community_centrality","peak_infection")
plots_enclc_ks_total <- lapply(1:8,createBivariatePlot,"communities_LinkComm","k_shell","total_infection")
plots_enclc_ks_peak <- lapply(1:8,createBivariatePlot,"communities_LinkComm","k_shell","peak_infection")
plots_enclc_cc_total <- lapply(1:8,createBivariatePlot,"communities_LinkComm","community_centrality","total_infection")
plots_enclc_cc_peak <- lapply(1:8,createBivariatePlot,"communities_LinkComm","community_centrality","peak_infection")
plots_cc_ks_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_cc_ks_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_cc_d_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_cc_d_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclv_ks_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclv_ks_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclv_cc_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclv_cc_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclc_ks_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclc_ks_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclc_cc_total
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








plots_enclc_cc_peak
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]








LS0tCnRpdGxlOiAiQ3JlYXRpb24gb2YgcmVsZXZhbnQgUGxvdHMgYW5kIERhdGEgZm9yIEFOUyBTdWJtaXNzaW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCmBgYHtyfQojIyBsb2FkIHVwIHRoZSBwYWNrYWdlcyB3ZSB3aWxsIG5lZWQ6ICAKcmVxdWlyZShpZ3JhcGgpCnJlcXVpcmUoZHBseXIpCnJlcXVpcmUoaGVyZSkKcmVxdWlyZShnZ2NvcnJwbG90KQoKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpvcHRpb25zKHNjaXBlbiA9IDYsIGRpZ2l0cyA9IDQpICMgSSBwcmVmZXIgdG8gdmlldyBvdXRwdXRzIGluIG5vbi1zY2llbnRpZmljIG5vdGF0aW9uCnBhbGxldHRlIDwtIGMoIiM2NjdjNzQiLCAiI2IzNjkyYiIsICIjYzFiYTdmIiwgIiMxZjE5MTgiLCAiI2JiODA4MiIpIyB0aGlzIGlzIG5lZWRlZCBvbiBzb21lIFBDcyB0byBpbmNyZWFzZSBtZW1vcnkgYWxsb3dhbmNlLCBidXQgaGFzIG5vIGltcGFjdCBvbiBtYWMKCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tRnVuY3Rpb25zIG5lZWRlZApyZWFkQ1NWcyA8LSBmdW5jdGlvbihmaWxlbmFtZSl7CiAgb2JqZWN0IDwtIHJlYWQuY3N2KHBhc3RlMChoZXJlKCJkYXRhIiwiZGZzX2ZpbmFsIiksIi8iLGZpbGVuYW1lKSkKICByZXR1cm4ob2JqZWN0KQp9CiNUZXN0IGZvciBub3JtYWxpdHkKbm9ybWFsaXR5VGVzdCA8LSBmdW5jdGlvbihkZil7CiAgZGYgPC0gZGYKICBvYmplY3QgPC0gbGFwcGx5KHJlbGV2YW50Q29scyxub3JtYWxpdHlUZXN0X2luc2lkZSxkZikKfQoKbm9ybWFsaXR5VGVzdF9pbnNpZGUgPC0gZnVuY3Rpb24ocmVsLGRmKXsKICByZXR1cm4oc2hhcGlyby50ZXN0KGRmWyxyZWxdKSkKfQpnZXRQdmFsdWVJbnNpZGUgPC0gZnVuY3Rpb24oZGYpewogIGlmKGRmW1syXV08MC4yMCl7CiAgICByZXR1cm4oIm5vIG5vcm1hbGl0eSIpCiAgfSBlbHNlIGlmKGRmW1syXV0+MC4yMCl7CiAgICByZXR1cm4oIm5vcm1hbGl0eSBnaXZlbiIpCiAgfQogCn0KZ2V0UHZhbHVlIDwtIGZ1bmN0aW9uKGRmKXsKICBsaXN0IDwtIGxhcHBseShkZixnZXRQdmFsdWVJbnNpZGUpCiAgcmV0dXJuKGxpc3QpCn0KI0NyZWF0ZSBQbG90cwpjcmVhdGVQbG90IDwtIGZ1bmN0aW9uKGluZGV4KXsKICBjb3JyIDwtIHJvdW5kKGNvcihERnNbW2luZGV4XV0gJT4lCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoa19zaGVsbCxudW1fY2x1c3RlcnMsZGVncmVlLGNvbW11bml0eV9jZW50cmFsaXR5LHRvdGFsX2luZmVjdGlvbixjb21tdW5pdGllc19sb3V2YWluKQogICxtZXRob2QgPSAic3BlYXJtYW4iKSwgMikKICBwLm1hdCA8LSBjb3JfcG1hdChERnNbW2luZGV4XV0gJT4lCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoa19zaGVsbCxudW1fY2x1c3RlcnMsZGVncmVlLGNvbW11bml0eV9jZW50cmFsaXR5LHRvdGFsX2luZmVjdGlvbixjb21tdW5pdGllc19sb3V2YWluKSkKICAKICBwIDwtIGdnY29ycnBsb3QoY29yciwgaGMub3JkZXIgPSBUUlVFLCB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICAgIHAubWF0ID0gcC5tYXQsCiAgICAgICAgICAgICBsYWIgPSBUUlVFLAogICAgICAgICAgICAgb3V0bGluZS5jb2wgPSAid2hpdGUiLAogICAgICAgICAgICAgZ2d0aGVtZSA9IGdncGxvdDI6OnRoZW1lX2dyYXksCiAgICAgICAgICAgICBjb2xvcnMgPSBjKCIjNkQ5RUMxIiwgIndoaXRlIiwgIiNFNDY3MjYiKSkrbGFicyh0aXRsZSA9IERGc19maWxlc19saXN0W1tpbmRleF1dKQogIAogIHJldHVybihwKQp9CgpgYGAKCiMjIEltcG9ydCBOZXR3b3JrcwpGaXJzdCB0aGluZ3MgZmlyc3QuIExldCdzIGltcG9ydCB0aGUgbmV3bHkgZ2VuZXJhdGVkIERGcyB3aXRoIENvbW11bml0eSBTaXplIEluZm8KYGBge3J9CiNDcmVhdGUgbGlzdCBvZiBERnMKREZzX2ZpbGVzX2xpc3QgPC0gbGlzdC5maWxlcyhwYXN0ZTAoaGVyZSgiZGF0YSIsImRmc19maW5hbCIpLCIvIikpCgojSW1wb3J0IERGcwpERnMgPC0gbGFwcGx5KERGc19maWxlc19saXN0LHJlYWRDU1ZzKQoKCmBgYAoKIyMgQ2hlY2sgbm9ybWFsaXR5CkNoZWNrIHJlbGV2YW50IG1lYXN1cmVzIGZvciBhc3N1bXB0aW9uIG9mIG5vcm1hbGl0eQpgYGB7cn0KcmVsZXZhbnRDb2xzIDwtIGMoMiwzLDQsNSwxMCwxMSkKcmVsZXZhbnRDb2xOYW1lcyA8LSBjKCJrX3NoZWxsIiwiZGVncmVlIiwiY29tbXVuaXR5X2NlbnRyYWxpdHkiLCJudW1fY2x1c3RlcnMiLCJjb21tdW5pdGllc19sb3V2YWluIiwiY29tbXVuaXRpZXNfTGlua0NvbW0iKQoKc2F2ZSA8LSBsYXBwbHkoREZzLG5vcm1hbGl0eVRlc3QpCm5vcm1hbGl0aWVzIDwtIGxhcHBseShzYXZlLGdldFB2YWx1ZSkKbm9ybWFsaXRpZXMgPC0gbGFwcGx5KG5vcm1hbGl0aWVzLGFzLmRhdGEuZnJhbWUpCm5vcm1hbGl0aWVzIDwtIGxhcHBseShub3JtYWxpdGllcyxzZXROYW1lcyxyZWxldmFudENvbE5hbWVzKQoKbm9ybWFsaXRpZXMKYGBgCiMjIENvcnJlbGF0aW9ucwoKTm8gbm9ybWFsaXR5IGNhbiBiZSBhc3N1bWVkIGZvciBhbnkgb2YgdGhlIG1lYXN1cmVzLiBHZXQgQ29ycmVsYXRpb25zIG9mIEVOQ19sdiAoTG91dmFpbikgYW5kIEVOQ19sYyAoTGlua0NvbW0pIHdpdGggb3RoZXIgTWVhc3VyZXMKCmBgYHtyfQoKbWVhc3VyZXNWZWN0b3IgPC0gYygia19zaGVsbCIsImRlZ3JlZSIsImNvbW11bml0eV9jZW50cmFsaXR5IiwicGVha19pbmZlY3Rpb24iLCJ0b3RhbF9pbmZlY3Rpb24iKQoKZ2V0Q29ycmVsYXRpb25zX0VOQ19sdiA8LSBmdW5jdGlvbihkZil7CiAgb3V0cHV0IDwtIG51bWVyaWMoKQogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX2xvdXZhaW4sa19zaGVsbCxtZXRob2QgPSAic3BlYXJtYW4iKSkpKSAtPiBvdXRwdXQKICBjKG91dHB1dCxhcy5udW1lcmljKGRmICU+JQogICAgc3VtbWFyaXNlKGNvcihjb21tdW5pdGllc19sb3V2YWluLGRlZ3JlZSxtZXRob2QgPSAic3BlYXJtYW4iKSkpKSAtPiBvdXRwdXQKICBjKG91dHB1dCxhcy5udW1lcmljKGRmICU+JQogICAgc3VtbWFyaXNlKGNvcihjb21tdW5pdGllc19sb3V2YWluLGNvbW11bml0eV9jZW50cmFsaXR5LG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpIC0+IG91dHB1dAogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX2xvdXZhaW4scGVha19pbmZlY3Rpb24sbWV0aG9kID0gInNwZWFybWFuIikpKSkgLT4gb3V0cHV0CiAgIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX2xvdXZhaW4sdG90YWxfaW5mZWN0aW9uLG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpIC0+IG91dHB1dAogcmV0dXJuKGFzLm51bWVyaWMob3V0cHV0KSkKfQoKZ2V0Q29ycmVsYXRpb25zX0VOQ19sYyA8LSBmdW5jdGlvbihkZil7CiAgb3V0cHV0IDwtIG51bWVyaWMoKQogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX0xpbmtDb21tLGtfc2hlbGwsbWV0aG9kID0gInNwZWFybWFuIikpKSkgLT4gb3V0cHV0CiAgYyhvdXRwdXQsYXMubnVtZXJpYyhkZiAlPiUKICAgIHN1bW1hcmlzZShjb3IoY29tbXVuaXRpZXNfTGlua0NvbW0sZGVncmVlLG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpIC0+IG91dHB1dAogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX0xpbmtDb21tLGNvbW11bml0eV9jZW50cmFsaXR5LG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpIC0+IG91dHB1dAogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX0xpbmtDb21tLHBlYWtfaW5mZWN0aW9uLG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpIC0+IG91dHB1dAogIGMob3V0cHV0LGFzLm51bWVyaWMoZGYgJT4lCiAgICBzdW1tYXJpc2UoY29yKGNvbW11bml0aWVzX0xpbmtDb21tLHRvdGFsX2luZmVjdGlvbixtZXRob2QgPSAic3BlYXJtYW4iKSkpKSAtPiBvdXRwdXQKICByZXR1cm4oYXMubnVtZXJpYyhvdXRwdXQpKQp9CgoKY29ycl9kZl9sYyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKG1lYXN1cmVzVmVjdG9yLGFzLmRhdGEuZnJhbWUoc2FwcGx5KERGcyxnZXRDb3JyZWxhdGlvbnNfRU5DX2xjKSkpKQpjb3JyX2RmX2x2IDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQobWVhc3VyZXNWZWN0b3IsYXMuZGF0YS5mcmFtZShzYXBwbHkoREZzLGdldENvcnJlbGF0aW9uc19FTkNfbHYpKSkpCgpuYW1lcyhjb3JyX2RmX2xjKSA8LSBjKCJtZWFzdXJlIixERnNfZmlsZXNfbGlzdCkKbmFtZXMoY29ycl9kZl9sdikgPC0gYygibWVhc3VyZSIsREZzX2ZpbGVzX2xpc3QpCgpjb3JyX2RmX2xjCmNvcnJfZGZfbHYKYGBgCiMjIE1lYW4gYW5kIFNEIENvcnJlbGF0aW9ucwpHZXQgbWVhbiBjb3JyZWxhdGlvbiBhbmQgc2QgY29ycmVsYXRpb24gZm9yIGNvcnJfZGZfbGMgKExpbmtDb21tKSBhbmQgY29ycl9kZl9sdiAoTG91dmFpbikKYGBge3J9CmNvcnJfZGZfbGMgJT4lCiAgcm93d2lzZShtZWFzdXJlKSU+JQogIHN1bW1hcmlzZShtZWFuX2NvcnJfZGZfbGMgPSBtZWFuKGMobmV0d29ya18xLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya18yLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya18zLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya180LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya181LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya182LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya183LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya184LmNzdikpLAogICAgICAgICAgICBzZF9jb3JyX2RmX2xjPXNkKGMobmV0d29ya18xLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya18yLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya18zLmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya180LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya181LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya182LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya183LmNzdiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29ya184LmNzdikpKQoKY29ycl9kZl9sdiAlPiUKICByb3d3aXNlKG1lYXN1cmUpJT4lCiAgc3VtbWFyaXNlKG1lYW5fY29ycl9kZl9sdiA9IG1lYW4oYyhuZXR3b3JrXzEuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzIuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzMuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzQuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzUuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzYuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzcuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzguY3N2KSksCiAgICAgICAgICAgIHNkX2NvcnJfZGZfbHY9c2QoYyhuZXR3b3JrXzEuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzIuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzMuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzQuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzUuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzYuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzcuY3N2LAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrXzguY3N2KSkpCmBgYAojIyBQbG90cyBvZiBDb3JyZWxhdGlvbnMKCk5vdywgd2Ugd2FudCB0byBwbG90IHRoZXNlIHZhbHVlcy4KYGBge3J9CmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkod2VzYW5kZXJzb24pCgojcHJlcGFyZSBkZnMgZm9yIHBsb3QgKHJlbW92ZSBvbmUgY29sKQpjb3JyX2RmX2xjICU+JQogICAgICAgc2VsZWN0KC1tZWFzdXJlKSAtPiBjb3JyX2RmX2xjX3Bsb3QKY29ycl9kZl9sdiAlPiUKICAgICAgIHNlbGVjdCgtbWVhc3VyZSkgLT4gY29ycl9kZl9sdl9wbG90CgojcGxvdCBmaXJzdCB3aXRoIG1lbHQKY2JpbmQobWVsdChjb3JyX2RmX2xjX3Bsb3QsbWVhc3VyZS52YXJzID0gY29sbmFtZXMoY29ycl9kZl9sY19wbG90KSksbWVhc3VyZSA9IGFzLmZhY3RvcihtZWFzdXJlc1ZlY3RvcikpICU+JSBnZ3Bsb3QoLixhZXMoeD12YXJpYWJsZSx5PXZhbHVlLGNvbG9yPW1lYXN1cmUpKStnZW9tX2xpbmUoYWVzKGdyb3VwPW1lYXN1cmUpKStnZW9tX3BvaW50KGFlcyhjb2xvcj1tZWFzdXJlKSxzaXplPTMsc2hhcGUgPSAyMSwgCiAgICAgICAgICAgICAgIHN0cm9rZSA9IDIuMCxmaWxsPSJ3aGl0ZSIpK3NjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9cGFsbGV0dGUpK3lsYWIoIlNwZWFybWFuJ3MgQ29ycmVsYXRpb24iKSt4bGFiKCJuZXR3b3JrIikrbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbnMgb2YgRU5DX2xjIHdpdGggb3RoZXIgTWVhc3VyZXMiKQoKI3Bsb3Qgc2Vjb25kIHdpdGggbWVsdApjYmluZChtZWx0KGNvcnJfZGZfbHZfcGxvdCxtZWFzdXJlLnZhcnMgPSBjb2xuYW1lcyhjb3JyX2RmX2x2X3Bsb3QpKSxtZWFzdXJlID0gYXMuZmFjdG9yKG1lYXN1cmVzVmVjdG9yKSkgJT4lIGdncGxvdCguLGFlcyh4PXZhcmlhYmxlLHk9dmFsdWUsY29sb3I9bWVhc3VyZSkpK2dlb21fbGluZShhZXMoZ3JvdXA9bWVhc3VyZSkpK2dlb21fcG9pbnQoYWVzKGNvbG9yPW1lYXN1cmUpLHNpemU9MyxzaGFwZSA9IDIxLCAKICAgICAgICAgICAgICAgc3Ryb2tlID0gMi4wLGZpbGw9IndoaXRlIikrc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1wYWxsZXR0ZSkreWxhYigiU3BlYXJtYW4ncyBDb3JyZWxhdGlvbiIpK3hsYWIoIm5ldHdvcmsiKStsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9ucyBvZiBFTkNfbHYgd2l0aCBvdGhlciBNZWFzdXJlcyIpCmBgYAojQXZnLiBQZWFrIEluZmVjdGlvbiBhY3Jvc3MgTmV0d29ya3MKYGBge3J9CmdldEF2Z1BlYWtJbmZlY3Rpb24gPC0gZnVuY3Rpb24oZGYpewogIGRmJT4lCiAgc3VtbWFyaXNlKG1lYW4ocGVha19pbmZlY3Rpb24pKSAtPiBvdXRwdXQKICByZXR1cm4oYXMubnVtZXJpYyhvdXRwdXQpKQp9CgoKYXZnUGVha0luZmVjdGlvbnMgPC0gY2JpbmQoREZzX2ZpbGVzX2xpc3QsYXMuZGF0YS5mcmFtZShzYXBwbHkoREZzLGdldEF2Z1BlYWtJbmZlY3Rpb24pKSkKbmFtZXMoYXZnUGVha0luZmVjdGlvbnMpIDwtIGMoIm5ldHdvcmsiLCJhdmdQZWFrSW5mZWN0aW9uIikKCmF2Z1BlYWtJbmZlY3Rpb25zCmBgYAojI1Bsb3Qgb2YgYXZnLiBQZWFrIEluZmVjdGlvbnMKYGBge3J9CmF2Z1BlYWtJbmZlY3Rpb25zICU+JQogIGdncGxvdChhZXMoeD1uZXR3b3JrLHk9YXZnUGVha0luZmVjdGlvbikpK2dlb21fbGluZShhZXMoZ3JvdXAgPSAxKSxjb2xvcj0iI2IzNjkyYiIpK2dlb21fcG9pbnQoY29sb3I9IiNiMzY5MmIiLHNpemU9MyxzaGFwZSA9IDIxLHN0cm9rZSA9IDIuMCxmaWxsPSJ3aGl0ZSIpK3lsYWIoIlBlYWsgSW5mZWN0aW9uIikreGxhYigibmV0d29yayIpK2xhYnModGl0bGUgPSAiQXZnIFBlYWsgSW5mZWN0aW9uIGFjcm9zcyBuZXR3b3JrcyIpCiAgICAgICAgIAogICAgICAKYGBgCgojI0ltcHJlY2lzaW9uIEZ1bmN0aW9uCmBgYHtyfQpnZXRJbXByZWNpc2lvbl90b3RhbCA8LSBmdW5jdGlvbihkZil7CiAgICB0b3Bfc3ByZWFkZXIgPC0gYXMubnVtZXJpYyhkZiU+JQogICAgICAgICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKHRvdGFsX2luZmVjdGlvbikpJT4lCiAgICAgICAgICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UobWVhbih0b3RhbF9pbmZlY3Rpb24pKSkKICAgIG91dHB1dCA8LSBudW1lcmljKCkKICAgIGMob3V0cHV0LGFzLm51bWVyaWMoMS0oZGYlPiUKICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGtfc2hlbGwpKSU+JQogICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3BfbWVhc3VyZSA9IG1lYW4odG90YWxfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgYyhvdXRwdXQsYXMubnVtZXJpYygxLShkZiU+JQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZGVncmVlKSklPiUKICAgICAgICAgICAgICAgc2xpY2VfaGVhZChwcm9wID0gMC4xKSU+JQogICAgICAgICAgICAgICBzdW1tYXJpc2UodG9wX21lYXN1cmUgPSBtZWFuKHRvdGFsX2luZmVjdGlvbikpKS90b3Bfc3ByZWFkZXIpKSAtPiBvdXRwdXQKICAgIGMob3V0cHV0LGFzLm51bWVyaWMoMS0oZGYlPiUKICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGNvbW11bml0eV9jZW50cmFsaXR5KSklPiUKICAgICAgICAgICAgICAgc2xpY2VfaGVhZChwcm9wID0gMC4xKSU+JQogICAgICAgICAgICAgICBzdW1tYXJpc2UodG9wX21lYXN1cmUgPSBtZWFuKHRvdGFsX2luZmVjdGlvbikpKS90b3Bfc3ByZWFkZXIpKSAtPiBvdXRwdXQKICAgIGMob3V0cHV0LGFzLm51bWVyaWMoMS0oZGYlPiUKICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGNvbW11bml0aWVzX2xvdXZhaW4pKSU+JQogICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3BfbWVhc3VyZSA9IG1lYW4odG90YWxfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgYyhvdXRwdXQsYXMubnVtZXJpYygxLShkZiU+JQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoY29tbXVuaXRpZXNfTGlua0NvbW0pKSU+JQogICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3BfbWVhc3VyZSA9IG1lYW4odG90YWxfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgcmV0dXJuKG91dHB1dCkKfQoKZ2V0SW1wcmVjaXNpb25fcGVhayA8LSBmdW5jdGlvbihkZil7CiAgICB0b3Bfc3ByZWFkZXIgPC0gYXMubnVtZXJpYyhkZiU+JQogICAgICAgICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKHBlYWtfaW5mZWN0aW9uKSklPiUKICAgICAgICAgICAgICAgICAgICAgIHNsaWNlX2hlYWQocHJvcCA9IDAuMSklPiUKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShtZWFuKHBlYWtfaW5mZWN0aW9uKSkpCiAgICBvdXRwdXQgPC0gbnVtZXJpYygpCiAgICBjKG91dHB1dCxhcy5udW1lcmljKDEtKGRmJT4lCiAgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhrX3NoZWxsKSklPiUKICAgICAgICAgICAgICAgc2xpY2VfaGVhZChwcm9wID0gMC4xKSU+JQogICAgICAgICAgICAgICBzdW1tYXJpc2UodG9wX21lYXN1cmUgPSBtZWFuKHBlYWtfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgYyhvdXRwdXQsYXMubnVtZXJpYygxLShkZiU+JQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZGVncmVlKSklPiUKICAgICAgICAgICAgICAgc2xpY2VfaGVhZChwcm9wID0gMC4xKSU+JQogICAgICAgICAgICAgICBzdW1tYXJpc2UodG9wX21lYXN1cmUgPSBtZWFuKHBlYWtfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgYyhvdXRwdXQsYXMubnVtZXJpYygxLShkZiU+JQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoY29tbXVuaXR5X2NlbnRyYWxpdHkpKSU+JQogICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3BfbWVhc3VyZSA9IG1lYW4ocGVha19pbmZlY3Rpb24pKSkvdG9wX3NwcmVhZGVyKSkgLT4gb3V0cHV0CiAgICBjKG91dHB1dCxhcy5udW1lcmljKDEtKGRmJT4lCiAgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhjb21tdW5pdGllc19sb3V2YWluKSklPiUKICAgICAgICAgICAgICAgc2xpY2VfaGVhZChwcm9wID0gMC4xKSU+JQogICAgICAgICAgICAgICBzdW1tYXJpc2UodG9wX21lYXN1cmUgPSBtZWFuKHBlYWtfaW5mZWN0aW9uKSkpL3RvcF9zcHJlYWRlcikpIC0+IG91dHB1dAogICAgYyhvdXRwdXQsYXMubnVtZXJpYygxLShkZiU+JQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoY29tbXVuaXRpZXNfTGlua0NvbW0pKSU+JQogICAgICAgICAgICAgICBzbGljZV9oZWFkKHByb3AgPSAwLjEpJT4lCiAgICAgICAgICAgICAgIHN1bW1hcmlzZSh0b3BfbWVhc3VyZSA9IG1lYW4ocGVha19pbmZlY3Rpb24pKSkvdG9wX3NwcmVhZGVyKSkgLT4gb3V0cHV0CiAgICByZXR1cm4ob3V0cHV0KQp9CgptZWFzdXJlcyA8LSBjKCJrX3NoZWxsIiwiZGVncmVlIiwiY29tbXVuaXR5X2NlbnRyYWxpdHkiLCJjb21tdW5pdGllc19sb3V2YWluIiwiY29tbXVuaXRpZXNfTGlua0NvbW0iKQoKaW1wcmVjaXNpb25fdG90YWwgPC0gY2JpbmQobWVhc3VyZXMsYXMuZGF0YS5mcmFtZShzYXBwbHkoREZzLGdldEltcHJlY2lzaW9uX3RvdGFsKSkpCmltcHJlY2lzaW9uX3BlYWsgPC0gY2JpbmQobWVhc3VyZXMsYXMuZGF0YS5mcmFtZShzYXBwbHkoREZzLGdldEltcHJlY2lzaW9uX3BlYWspKSkKCm5hbWVzKGltcHJlY2lzaW9uX3RvdGFsKSA8LSBjKCJtZWFzdXJlIixERnNfZmlsZXNfbGlzdCkKbmFtZXMoaW1wcmVjaXNpb25fcGVhaykgPC0gYygibWVhc3VyZSIsREZzX2ZpbGVzX2xpc3QpCgppbXByZWNpc2lvbl9wZWFrCmltcHJlY2lzaW9uX3RvdGFsCmBgYAoKIyNJbXByZWNpc2lvbiBQbG90cwpOb3cgcGxvdCBpdCAKYGBge3J9CmltcHJlY2lzaW9uX3RvdGFsICU+JQogICAgICAgc2VsZWN0KC1tZWFzdXJlKSAtPiBpbXByZWNpc2lvbl90b3RhbF9wbG90CmltcHJlY2lzaW9uX3BlYWsgJT4lCiAgICAgICBzZWxlY3QoLW1lYXN1cmUpIC0+IGltcHJlY2lzaW9uX3BlYWtfcGxvdAoKI3Bsb3QgZmlyc3Qgd2l0aCBtZWx0CmNiaW5kKG1lbHQoaW1wcmVjaXNpb25fdG90YWxfcGxvdCxtZWFzdXJlLnZhcnMgPSBjb2xuYW1lcyhpbXByZWNpc2lvbl90b3RhbF9wbG90KSksbWVhc3VyZSA9IGFzLmZhY3RvcihtZWFzdXJlcykpICU+JSBnZ3Bsb3QoLixhZXMoeD12YXJpYWJsZSx5PXZhbHVlLGNvbG9yPW1lYXN1cmUpKStnZW9tX2xpbmUoYWVzKGdyb3VwPW1lYXN1cmUpKStnZW9tX3BvaW50KGFlcyhjb2xvcj1tZWFzdXJlKSxzaXplPTMsc2hhcGUgPSAyMSwgCiAgICAgICAgICAgICAgIHN0cm9rZSA9IDIuMCxmaWxsPSJ3aGl0ZSIpK3NjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9cGFsbGV0dGUpK3lsYWIoIkltcHJlY2lzaW9uIikreGxhYigibmV0d29yayIpK2xhYnModGl0bGUgPSAiSW1wcmVjaXNpb24gRnVuY3Rpb24gKHRvdGFsX2luZmVjdGlvbikiKQoKI3Bsb3Qgc2Vjb25kIHdpdGggbWVsdApjYmluZChtZWx0KGltcHJlY2lzaW9uX3BlYWtfcGxvdCxtZWFzdXJlLnZhcnMgPSBjb2xuYW1lcyhpbXByZWNpc2lvbl9wZWFrX3Bsb3QpKSxtZWFzdXJlID0gYXMuZmFjdG9yKG1lYXN1cmVzKSkgJT4lIGdncGxvdCguLGFlcyh4PXZhcmlhYmxlLHk9dmFsdWUsY29sb3I9bWVhc3VyZSkpK2dlb21fbGluZShhZXMoZ3JvdXA9bWVhc3VyZSkpK2dlb21fcG9pbnQoYWVzKGNvbG9yPW1lYXN1cmUpLHNpemU9MyxzaGFwZSA9IDIxLCAKICAgICAgICAgICAgICAgc3Ryb2tlID0gMi4wLGZpbGw9IndoaXRlIikrc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1wYWxsZXR0ZSkreWxhYigiSW1wcmVjaXNpb24iKSt4bGFiKCJuZXR3b3JrIikrbGFicyh0aXRsZSA9ICJJbXByZWNpc2lvbiBGdW5jdGlvbiAocGVha19pbmZlY3Rpb24pIikKYGBgCgojI0JpdmFyaWF0ZSBQbG90cwpOb3cgd2Ugd2FudCB0byB0YWtlIGEgbG9vayBhdCB0aGUgYml2YXJpYXRlIHBsb3RzLiBGaXJzdGx5LCBjcmVhdGUgdGhlIGZ1bmN0aW9ucy4KCmBgYHtyfQpsaWJyYXJ5KHZpcmlkaXMpCgpjcmVhdGVCaXZhcmlhdGVQbG90IDwtIGZ1bmN0aW9uKGluZGV4LG1lYXN1cmVfeCxtZWFzdXJlX3ksaW5mZWN0aW9uKXsKICAKICBERnNbW2luZGV4XV0lPiUKICBnZ3Bsb3QoYWVzX3N0cmluZyh4PW1lYXN1cmVfeCx5PW1lYXN1cmVfeSkpKwogIGdlb21faml0dGVyKGFlc19zdHJpbmcoY29sb3I9aW5mZWN0aW9uKSkrc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiaW5mZXJubyIpKwogIHRoZW1lX21pbmltYWwoKStsYWJzKHRpdGxlID0gREZzX2ZpbGVzX2xpc3RbaW5kZXhdKS0+cGxvdAogIAogIHJldHVybihwbG90KQp9CmBgYAoKTm93LCBjcmVhdGUgdGhlIHBsb3RzCgpgYGB7cn0KcGxvdHNfY2Nfa3NfdG90YWwgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdHlfY2VudHJhbGl0eSIsImtfc2hlbGwiLCJ0b3RhbF9pbmZlY3Rpb24iKQpwbG90c19jY19rc19wZWFrIDwtIGxhcHBseSgxOjgsY3JlYXRlQml2YXJpYXRlUGxvdCwiY29tbXVuaXR5X2NlbnRyYWxpdHkiLCJrX3NoZWxsIiwicGVha19pbmZlY3Rpb24iKQpwbG90c19jY19kX3RvdGFsIDwtIGxhcHBseSgxOjgsY3JlYXRlQml2YXJpYXRlUGxvdCwiY29tbXVuaXR5X2NlbnRyYWxpdHkiLCJkZWdyZWUiLCJ0b3RhbF9pbmZlY3Rpb24iKQpwbG90c19jY19kX3BlYWsgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdHlfY2VudHJhbGl0eSIsImRlZ3JlZSIsInBlYWtfaW5mZWN0aW9uIikKcGxvdHNfZW5jbHZfa3NfdG90YWwgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdGllc19sb3V2YWluIiwia19zaGVsbCIsInRvdGFsX2luZmVjdGlvbiIpCnBsb3RzX2VuY2x2X2tzX3BlYWsgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdGllc19sb3V2YWluIiwia19zaGVsbCIsInBlYWtfaW5mZWN0aW9uIikKcGxvdHNfZW5jbHZfY2NfdG90YWwgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdGllc19sb3V2YWluIiwiY29tbXVuaXR5X2NlbnRyYWxpdHkiLCJ0b3RhbF9pbmZlY3Rpb24iKQpwbG90c19lbmNsdl9jY19wZWFrIDwtIGxhcHBseSgxOjgsY3JlYXRlQml2YXJpYXRlUGxvdCwiY29tbXVuaXRpZXNfbG91dmFpbiIsImNvbW11bml0eV9jZW50cmFsaXR5IiwicGVha19pbmZlY3Rpb24iKQpwbG90c19lbmNsY19rc190b3RhbCA8LSBsYXBwbHkoMTo4LGNyZWF0ZUJpdmFyaWF0ZVBsb3QsImNvbW11bml0aWVzX0xpbmtDb21tIiwia19zaGVsbCIsInRvdGFsX2luZmVjdGlvbiIpCnBsb3RzX2VuY2xjX2tzX3BlYWsgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdGllc19MaW5rQ29tbSIsImtfc2hlbGwiLCJwZWFrX2luZmVjdGlvbiIpCnBsb3RzX2VuY2xjX2NjX3RvdGFsIDwtIGxhcHBseSgxOjgsY3JlYXRlQml2YXJpYXRlUGxvdCwiY29tbXVuaXRpZXNfTGlua0NvbW0iLCJjb21tdW5pdHlfY2VudHJhbGl0eSIsInRvdGFsX2luZmVjdGlvbiIpCnBsb3RzX2VuY2xjX2NjX3BlYWsgPC0gbGFwcGx5KDE6OCxjcmVhdGVCaXZhcmlhdGVQbG90LCJjb21tdW5pdGllc19MaW5rQ29tbSIsImNvbW11bml0eV9jZW50cmFsaXR5IiwicGVha19pbmZlY3Rpb24iKQoKcGxvdHNfY2Nfa3NfdG90YWwKcGxvdHNfY2Nfa3NfcGVhawpwbG90c19jY19kX3RvdGFsCnBsb3RzX2NjX2RfcGVhawpwbG90c19lbmNsdl9rc190b3RhbApwbG90c19lbmNsdl9rc19wZWFrCnBsb3RzX2VuY2x2X2NjX3RvdGFsCnBsb3RzX2VuY2x2X2NjX3BlYWsKcGxvdHNfZW5jbGNfa3NfdG90YWwKcGxvdHNfZW5jbGNfa3NfcGVhawpwbG90c19lbmNsY19jY190b3RhbApwbG90c19lbmNsY19jY19wZWFrCmBgYAo=